#include "CoordDraw.h"
#include <cstdint>
#include <glad/glad.h>
#include "IShaderFormFile.h"
#include "Singleton.h"
#include "GlobalData.h"

using namespace gl;

class AxisDrawShader : public IShaderFormFile
{
public:
    AxisDrawShader() : IShaderFormFile("shader/axis.vert", "shader/axis.frag", "AxisDraw") {}
};

class GroundDrawShader : public IShaderFormFile
{
public:
    GroundDrawShader() : IShaderFormFile("shader/ground.vert", "shader/ground.frag", "GroundDraw") {}
};

struct CoordDrawImpl
{
    uint32_t groundVAO;
    uint32_t groundVBO;

    uint32_t axisVAO;
    uint32_t axisVBO;

    IShader* axisShader;
    IShader* groundShader;

    ~CoordDrawImpl()
    {
        delete axisShader;
        delete groundShader;
    }

    void CreateVAO()
    {
        const float groundVertex[] = {
            -100.0f, 0.0f, -100.0f,
            -100.0f, 0.0f, 100.0f,
            100.0f, 0.0f, -100.0f,
            100.0f, 0.0f, 100.0f,
            100.0f, 0.0f, -100.0f,
            -100.0f, 0.0f, 100.0f
        };
        glGenVertexArrays(1, &groundVAO);
        glGenBuffers(1, &groundVBO);
        glBindVertexArray(groundVAO);
        glBindBuffer(GL_ARRAY_BUFFER, groundVBO);
        glBufferData(GL_ARRAY_BUFFER, sizeof(groundVertex), groundVertex, GL_STATIC_DRAW);
        glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)nullptr);
        glEnableVertexAttribArray(0);
        glBindVertexArray(0);

        const float axisVertex[] = {
            0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f,
            100.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f,
            0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f,
            0.0f, 0.0f, -100.0f, 0.0f, 1.0f, 0.0f, 0.0f,
            0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 1.0f,
            0.0f, 100.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f
        };
        glGenVertexArrays(1, &axisVAO);
        glGenBuffers(1, &axisVBO);
        glBindVertexArray(axisVAO);
        glBindBuffer(GL_ARRAY_BUFFER, axisVBO);
        glBufferData(GL_ARRAY_BUFFER, sizeof(axisVertex), axisVertex, GL_STATIC_DRAW);
        glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 7 * sizeof(float), (void*)nullptr);
        glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, 7 * sizeof(float), (void*)(3 * sizeof(float)));
        glEnableVertexAttribArray(0);
        glEnableVertexAttribArray(1);
        glBindVertexArray(0);
    }
    void CreateShader()
    {
        axisShader = new AxisDrawShader();
        axisShader->UseShader();
        axisShader->setMat4("projMatrix", Singleton<GlobalData>::Instance().ProjMatrix);
        groundShader = new GroundDrawShader();
        groundShader->UseShader();
        groundShader->setMat4("projMatrix", Singleton<GlobalData>::Instance().ProjMatrix);
        groundShader->setVec4("plantColor", glm::vec4(0.5f, 0.5f, 0.5f, 1.0f));
        groundShader->setVec4("lineColor", glm::vec4(0.2f, 0.2f, 0.2f, 1.0f));
    }
};

CoordDraw::CoordDraw()
{
    impl = new CoordDrawImpl();
    impl->CreateShader();
    impl->CreateVAO();
}

CoordDraw::~CoordDraw()
{
    delete impl;
}

void CoordDraw::Render()
{
    if (Singleton<GlobalData>::Instance().DrawGround)
    {
        impl->groundShader->UseShader();
        impl->groundShader->setMat4("viewMatrix", Singleton<GlobalData>::Instance().ViewMatrix);
        glBindVertexArray(impl->groundVAO);
        glDrawArrays(GL_TRIANGLES, 0, 6);
    }

    if (Singleton<GlobalData>::Instance().DrawAxis)
    {
        impl->axisShader->UseShader();
        impl->axisShader->setMat4("viewMatrix", Singleton<GlobalData>::Instance().ViewMatrix);
        glLineWidth(5);
        glBindVertexArray(impl->axisVAO);
        glDrawArrays(GL_LINES, 0, 6);
    }
    glBindVertexArray(0);
}
